package com.zxy.ziems.server.energy.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.zxy.btp.common.response.PageResult;
import com.zxy.btp.web.util.TokenUtils;
import com.zxy.ziems.server.classify.pojo.entity.ClassifyDeviceProperty;
import com.zxy.ziems.server.classify.pojo.param.ClassifyMeasureDataHourParam;
import com.zxy.ziems.server.classify.service.ClassifyDevicePropertyService;
import com.zxy.ziems.server.constant.enums.CalculateTypeEnum;
import com.zxy.ziems.server.device.mapper.DeviceMapper;
import com.zxy.ziems.server.device.pojo.dto.DeviceMeasureDataHourDTO;
import com.zxy.ziems.server.device.pojo.entity.DataTypeEntity;
import com.zxy.ziems.server.device.pojo.entity.DeviceEntity;
import com.zxy.ziems.server.device.pojo.vo.DeviceMeasureDataHourVO;
import com.zxy.ziems.server.device.service.DataTypeService;
import com.zxy.ziems.server.device.service.DeviceService;
import com.zxy.ziems.server.domain.DeviceMeasureDataIncrementHour;
import com.zxy.ziems.server.energy.mapper.DeviceMeasureDataDayMapper;
import com.zxy.ziems.server.energy.mapper.DeviceMeasureDataHistoryMapper;
import com.zxy.ziems.server.energy.mapper.DeviceMeasureDataIncrementHourMapper;
import com.zxy.ziems.server.energy.mapper.DeviceMeasureDataMapper;
import com.zxy.ziems.server.energy.pojo.dto.DeviceMeasureDataDayDTO;
import com.zxy.ziems.server.energy.pojo.dto.DeviceMeasureDayCostDTO;
import com.zxy.ziems.server.energy.pojo.entity.DeviceMeasureDataDayEntity;
import com.zxy.ziems.server.energy.pojo.entity.DeviceMeasureDataHistoryEntity;
import com.zxy.ziems.server.energy.pojo.param.DeviceMeasureDataDayPageParam;
import com.zxy.ziems.server.energy.pojo.param.DeviceMeasureDataIncrementHourPageParam;
import com.zxy.ziems.server.energy.pojo.vo.DeviceMeasureDataDayVO;
import com.zxy.ziems.server.energy.pojo.vo.DeviceMeasureDataIncrementHourVO;
import com.zxy.ziems.server.energy.service.DeviceMeasureDataHistoryService;
import com.zxy.ziems.server.energy.service.DeviceMeasureDataIncrementHourService;
import com.zxy.ziems.server.energy.service.EnergyPriceService;
import com.zxy.ziems.server.log.DataRecordTypeEnum;
import com.zxy.ziems.server.log.pojo.entity.DataRecordLogEntity;
import com.zxy.ziems.server.log.service.DataRecordLogService;
import com.zxy.ziems.server.log.service.DataRecordService;
import com.zxy.ziems.server.mybatis.core.service.ServiceImplX;
import com.zxy.ziems.server.utils.AssertUtil;
import com.zxy.ziems.server.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @Copyright (C),
 * @ClassName: DeviceMeasureDataIncrementHourServiceImpl
 * @Author: yanzhiwei
 * @Description:增量小时值
 * @Date: 2024/4/7 14:48
 */
@Service
@Slf4j
public class DeviceMeasureDataIncrementHourServiceImpl extends ServiceImplX<DeviceMeasureDataIncrementHourMapper, DeviceMeasureDataIncrementHour> implements DeviceMeasureDataIncrementHourService {

    @Autowired
    private EnergyPriceService energyPriceService;
    @Autowired
    private DeviceMeasureDataDayMapper dataDayMapper;
    @Autowired
    private ClassifyDevicePropertyService classifyDevicePropertyService;
    @Autowired
    private DataRecordService dataRecordService;
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private DataTypeService dataTypeService;
    @Autowired
    private DeviceMeasureDataIncrementHourService deviceMeasureDataIncrementHourService;
    @Autowired
    private DeviceMeasureDataHistoryService deviceMeasureDataHistoryService;
    @Autowired
    private DeviceMeasureDataHistoryMapper deviceMeasureDataHistoryMapper;
    @Autowired
    private DeviceMeasureDataIncrementHourMapper deviceMeasureDataIncrementHourMapper;
    @Autowired
    private DeviceMapper deviceMapper;

    @Override
    public PageResult<DeviceMeasureDataHourVO> getDeviceMeasureDataHour(ClassifyMeasureDataHourParam param) {
        PageResult<DeviceMeasureDataHourVO> res = customPage(param, (page, pageParam) -> baseMapper.pageList(page, pageParam));
        if (res.getTotal() > 0) {
            List<DeviceMeasureDataHourVO> list = res.getList();
            // 获取上周期数据
            DeviceMeasureDataHourVO deviceMeasureDataHourVO = list.get(0);
            LocalDateTime localDateTime = deviceMeasureDataHourVO.getMeasureTime().plusHours(-1);
            param.setMeasureTimeStart(localDateTime);
            param.setMeasureTimeEnd(localDateTime);
            DeviceMeasureDataHourVO old = baseMapper.getVO(param); // 上周期
            // 计算上周期及变化率
            for (int i = 0; i < list.size(); i++) {
                DeviceMeasureDataHourVO cur = list.get(i);
                if (i > 0) old = list.get(i - 1);
                if (old == null) continue;
                cur.setUpMeasureValue(old.getMeasureValue());
                cur.setChangeRate(old.getMeasureValue().equals(BigDecimal.ZERO) ? null : cur.getMeasureValue().subtract(old.getMeasureValue()).divide(old.getMeasureValue(), 2, RoundingMode.HALF_UP));
            }
        }
        return res;
    }

    @Override
    public List<DeviceMeasureDayCostDTO> getDayPrice(LocalDate date) {
        return baseMapper.getDayPrice(DateUtils.getStartTime(date), DateUtils.getEndTime(date));
    }

    @Override
    public BigDecimal getDayPrice(LocalDate date, String dataTypeId, String deviceId) {
        AssertUtil.isFalse(date.compareTo(LocalDate.now().plusDays(-1)) > 0, "只能统计T-1的数据");
        return baseMapper.getDayPriceByDeviceAndDataType(date, dataTypeId, deviceId);
    }

    /**
     * 修改当前小时值，修改当前小时增量，修改下一小时增量
     * 修改增量消费
     * 修改天消费，如果修改的小时是最后一小时，则还需要修改天增量，及第二天增量
     * 如果一段时间的当前值都小于修改条目的当前值，将这段时间的当前值改成修改后的当前值，直到大于当前值为止，消费和天数据同样跟随修改
     *
     * @param dto
     */
    @Transactional
    @Override
    public void modifyMeasureDataHour(DeviceMeasureDataHourDTO dto) {
        // 获取当前值
        DeviceMeasureDataIncrementHour old = processBeforeCheckAndGetOldData(dto);
        // 数据区分实时值和增量值
        ClassifyDeviceProperty classifyDeviceProperty = classifyDevicePropertyService.getByClassifyIdAndDeviceIdAndDataTypeId(dto.getClassifyId(), dto.getDeviceId(), dto.getDataTypeId());
        AssertUtil.nonNull(classifyDeviceProperty, "未找设备属性计算方式");
        boolean isIncrement = classifyDeviceProperty.getCalculateType().equals(CalculateTypeEnum.INCREMENT.getCode());
        if (isIncrement) {
            // 增量值
            boolean decrement = old.getMeasureValue().compareTo(dto.getMeasureValue()) > 0;
            decrementProcessHandler(dto, old, decrement);
        } else {
            // 实时值，紧修改即可
            old.setMeasureValue(dto.getMeasureValue());
            baseMapper.updateById(old);
        }

    }

    @NotNull
    private DeviceMeasureDataIncrementHour processBeforeCheckAndGetOldData(DeviceMeasureDataHourDTO dto) {
        QueryWrapper<DeviceMeasureDataIncrementHour> query = new QueryWrapper<>();
        query.lambda().eq(DeviceMeasureDataIncrementHour::getId, dto.getId())
                .eq(DeviceMeasureDataIncrementHour::getDeviceId, dto.getDeviceId())
                .eq(DeviceMeasureDataIncrementHour::getDataTypeId, dto.getDataTypeId());
        DeviceMeasureDataIncrementHour old = baseMapper.selectOne(query);
        AssertUtil.nonNull(old, "未找到修改数据");
        AssertUtil.isFalse(dto.getMeasureValue().equals(old.getMeasureValue()), "数据未修改");
        return old;
    }

    private void decrementProcessHandler(DeviceMeasureDataHourDTO dto, DeviceMeasureDataIncrementHour old, boolean decrement) {
        // 当前值小于历史值时，获取上一时间节点（上小时）数据，当前值不可小于上一时间值，可等于
        LocalDateTime lastDateTime = old.getMeasureTime().plusHours(-1);
        LocalDateTime nextDateTime = old.getMeasureTime().plusHours(1);
        Map<LocalDateTime, DeviceMeasureDataIncrementHour> hourMap = getHourMap(old, dto, lastDateTime, nextDateTime, decrement);
        // 重新计算前面值和后面值
        DeviceMeasureDataIncrementHour last = hourMap.get(lastDateTime);
        DeviceMeasureDataIncrementHour next = hourMap.get(nextDateTime);
        if (decrement) {
            AssertUtil.isFalse(last.getMeasureValue().compareTo(dto.getMeasureValue()) < 0, "此值小于上周期值，请修改上周期值");
        } else {
            for (Map.Entry<LocalDateTime, DeviceMeasureDataIncrementHour> entry : hourMap.entrySet()) {
                next = entry.getValue();
            }
            hourMap.remove(last.getMeasureTime());
            AssertUtil.isFalse(next != null && next.getMeasureValue().compareTo(dto.getMeasureValue()) < 0, "此值大于最近大于当前值数据，请确认数据");

            // 修改中间数据，将值设置为修改值
            Set<String> ids = hourMap.values().stream().map(DeviceMeasureDataIncrementHour::getId).collect(Collectors.toSet());
            UpdateWrapper<DeviceMeasureDataIncrementHour> update = new UpdateWrapper<>();
            update.lambda().set(DeviceMeasureDataIncrementHour::getMeasureValue, dto.getMeasureValue())
                    .in(DeviceMeasureDataIncrementHour::getId, ids);
            baseMapper.update(update);
        }
        BigDecimal curPrice = energyPriceService.getEnergyPriceByDataTypeId(dto.getDataTypeId(), old.getMeasureTime());
        BigDecimal curIncrementValue = dto.getMeasureValue().subtract(last.getMeasureValue());
        old.setIncrementValue(curIncrementValue);
        old.setIncrementCost(curPrice != null ? curIncrementValue.multiply(curPrice) : null);
        old.setMeasureValue(dto.getMeasureValue());
        BigDecimal nextIncrementValue = next.getMeasureValue().subtract(dto.getMeasureValue());
        BigDecimal nextPrice = energyPriceService.getEnergyPriceByDataTypeId(dto.getDataTypeId(), next.getMeasureTime());
        next.setIncrementValue(nextIncrementValue);
        next.setIncrementCost(nextPrice);
        baseMapper.updateById(old);
        baseMapper.updateById(next);
        // 天数据修改
        dayDataModify(old, next);

    }

    private void dayDataModify(DeviceMeasureDataIncrementHour old, DeviceMeasureDataIncrementHour next) {
        LocalDate oldDate = old.getMeasureTime().toLocalDate();
        LocalDate nextDate = next.getMeasureTime().toLocalDate();
        BigDecimal dayPrice = getDayPrice(oldDate, old.getDataTypeId(), old.getDeviceId());
        updateDayPrice(dayPrice, old.getDeviceId(), old.getDataTypeId());
        if (!oldDate.equals(nextDate)) {
            BigDecimal nextDayPrice = getDayPrice(nextDate, old.getDataTypeId(), old.getDeviceId());
            updateDayPrice(nextDayPrice, old.getDeviceId(), old.getDataTypeId());
        }
    }

    private void updateDayPrice(BigDecimal dayPrice, String deviceId, String dataTypeId) {
        if (dayPrice == null) return;

        UpdateWrapper<DeviceMeasureDataDayEntity> updateParam = new UpdateWrapper<>();
        updateParam.lambda().set(DeviceMeasureDataDayEntity::getIncrementCost, dayPrice)
                .eq(DeviceMeasureDataDayEntity::getDeviceId, deviceId).eq(DeviceMeasureDataDayEntity::getDataTypeId, dataTypeId);
        dataDayMapper.update(updateParam);
    }


    @NotNull
    private Map<LocalDateTime, DeviceMeasureDataIncrementHour> getHourMap(DeviceMeasureDataIncrementHour old, DeviceMeasureDataHourDTO dto, LocalDateTime lastDateTime,
                                                                          LocalDateTime nextDateTime, boolean decrement) {
        List<DeviceMeasureDataIncrementHour> datas = new ArrayList<>();
        if (decrement) {
            QueryWrapper<DeviceMeasureDataIncrementHour> lastQuery = new QueryWrapper<>();
            lastQuery.lambda().eq(DeviceMeasureDataIncrementHour::getDeviceId, old.getDeviceId())
                    .eq(DeviceMeasureDataIncrementHour::getDataTypeId, old.getDataTypeId())
                    .in(DeviceMeasureDataIncrementHour::getMeasureTime, Arrays.asList(lastDateTime, nextDateTime));
            datas = baseMapper.selectList(lastQuery);
        } else {
            QueryWrapper<DeviceMeasureDataIncrementHour> lastQuery = new QueryWrapper<>();
            lastQuery.lambda().eq(DeviceMeasureDataIncrementHour::getDeviceId, old.getDeviceId())
                    .eq(DeviceMeasureDataIncrementHour::getDataTypeId, old.getDataTypeId())
                    .eq(DeviceMeasureDataIncrementHour::getMeasureTime, lastDateTime);
            DeviceMeasureDataIncrementHour last = baseMapper.selectOne(lastQuery);
            datas.add(last);
            datas.addAll(baseMapper.getChangeData(old.getDeviceId(), old.getDataTypeId(), old.getMeasureTime(), dto.getMeasureValue()));
        }

        Map<LocalDateTime, DeviceMeasureDataIncrementHour> hourMap = datas.stream()
                .collect(Collectors.toMap(DeviceMeasureDataIncrementHour::getMeasureTime, v -> v, (e, r) -> r, LinkedHashMap::new));
        return hourMap;
    }

    @Override
    public PageResult<DeviceMeasureDataHourVO> getMeasureData(ClassifyMeasureDataHourParam param) {

        PageResult<DeviceMeasureDataHourVO> res = customPage(param, (page, pageParam) -> baseMapper.getMeasureData(page, pageParam));
        if (res.getTotal() < 1) {
            return new PageResult<>(0L, new ArrayList<>());
        }
        // 上一周期时间
        LocalDateTime measureTimeStart = param.getMeasureTimeStart();
        LocalDateTime measureTimeEnd = param.getMeasureTimeEnd();
        LocalDateTime last = getLastHour(measureTimeStart, measureTimeEnd);

        Set<String> deviceIdSet = res.getList().stream().map(DeviceMeasureDataHourVO::getDeviceId).collect(Collectors.toSet());
        Set<String> dataTypeIdSet = res.getList().stream().map(DeviceMeasureDataHourVO::getDataTypeId).collect(Collectors.toSet());
        param.setMeasureTimeStart(last);
        param.setMeasureTimeEnd(measureTimeStart);
        param.setDeviceId(null);
        param.setDataTypeId(null);
        param.setDeviceIds(deviceIdSet);
        param.setDataTypeIds(dataTypeIdSet);
        PageResult<DeviceMeasureDataHourVO> lastRes = customPage(param, (page, pageParam) -> baseMapper.getMeasureData(page, pageParam));
        Map<String, DeviceMeasureDataHourVO> lastMap = lastRes.getList().stream().collect(Collectors.toMap(k -> k.getDeviceId() + "-" + k.getDataTypeId(), v -> v));
        List<DeviceMeasureDataHourVO> list = res.getList();
        list.forEach(l -> {
            l.setQueryTime(DateUtils.dateToString(measureTimeStart, DateUtils.PATTERN) + "~" + DateUtils.dateToString(measureTimeEnd, DateUtils.PATTERN));
            // 获取上周期数据
            DeviceMeasureDataHourVO old = lastMap.get(l.getDeviceId() + "-" + l.getDataTypeId());
            // 计算上周期及变化率
            l.setUpMeasureValue(old == null ? null : old.getMeasureValue());
            l.setChangeRate(old == null || old.getMeasureValue().compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ONE : l.getMeasureValue().subtract(old.getMeasureValue()).divide(old.getMeasureValue(), 2, RoundingMode.HALF_UP));
        });
        return res;
    }

    /**
     * 获取上周期小时数据
     *
     * @param measureTimeStart
     * @param measureTimeEnd
     * @return
     */
    @NotNull
    private static LocalDateTime getLastHour(LocalDateTime measureTimeStart, LocalDateTime measureTimeEnd) {
        long between = ChronoUnit.HOURS.between(measureTimeEnd, measureTimeStart);
        LocalDateTime last = measureTimeStart.plusHours(between);
        return last;
    }

    /**
     * 获取下周期小时数据
     *
     * @param measureTimeStart
     * @param measureTimeEnd
     * @return
     */
    @NotNull
    private static LocalDateTime getNextHour(LocalDateTime measureTimeStart, LocalDateTime measureTimeEnd) {
        long between = ChronoUnit.HOURS.between(measureTimeEnd, measureTimeStart);
        return measureTimeEnd.plusHours(-between);
    }

    /**
     * 修改数据
     * 当前时间数据按照上周期数据每小时的增量进行增加，及当前时间的增量，和上周期的增量相同
     * 同时费用和天增量及费用相应变化，且增加变化数据
     *
     * @param dtos
     */
    @Transactional
    @Override
    public void modifyMeasureData(List<DeviceMeasureDataHourDTO> dtos) {
        dtos.forEach(l -> {
            // 获取当前期数据
            LocalDateTime measureTimeStart = l.getMeasureTimeStart();
            LocalDateTime measureTimeEnd = l.getMeasureTimeEnd();
            List<DeviceMeasureDataIncrementHour> oldData = getOldData(l);
            // 获取上周期数据
            LocalDateTime lastTime = getLastHour(measureTimeStart, measureTimeEnd);
            l.setMeasureTimeStart(lastTime);
            l.setMeasureTimeEnd(measureTimeStart);
            List<DeviceMeasureDataIncrementHour> lastData = getOldData(l);
            // 获取下周期数据
            LocalDateTime nextHour = getNextHour(measureTimeStart, measureTimeEnd);
            l.setMeasureTimeStart(measureTimeEnd);
            l.setMeasureTimeEnd(nextHour);
            List<DeviceMeasureDataIncrementHour> nextData = getOldData(l);

            AssertUtil.isFalse(CollectionUtils.isEmpty(lastData), "未找到上周期数据");

            AssertUtil.isFalse(oldData.size() != lastData.size(), "未匹配到上周数据");
            BigDecimal lastVal = lastData.get(lastData.size() - 1).getMeasureValue();
            List<DataRecordLogEntity> logs = new ArrayList<>();
            Set<LocalDate> changeDay = new HashSet<>(); // 变化的天数，用于修改天数据费用、增量和值
            // 修改费用和数据
            for (int i = 0; i < oldData.size(); i++) {

                DeviceMeasureDataIncrementHour old = oldData.get(i);
                DeviceMeasureDataIncrementHour last = lastData.get(i);
                BigDecimal incrementValue = last.getIncrementValue();
                BigDecimal curPrice = energyPriceService.getEnergyPriceByDataTypeId(old.getDataTypeId(), old.getMeasureTime());
                BigDecimal incrementCost = curPrice == null ? null : incrementValue.multiply(curPrice).setScale(4);
                lastVal = lastVal.add(incrementValue);
                changeDay.add(old.getMeasureTime().toLocalDate());

                addLogs(lastVal, logs, old, incrementValue, incrementCost);

                old.setIncrementValue(incrementValue);
                old.setIncrementCost(incrementCost);
                old.setMeasureValue(lastVal);
                baseMapper.updateById(old);
            }
            // 修改最后一条数据
            if (!CollectionUtils.isEmpty(nextData)) {
                DeviceMeasureDataIncrementHour next = nextData.get(0);
                BigDecimal incrementValue = next.getMeasureValue().subtract(lastVal);
                BigDecimal curPrice = energyPriceService.getEnergyPriceByDataTypeId(next.getDataTypeId(), next.getMeasureTime());
                BigDecimal incrementCost = curPrice == null ? null : incrementValue.multiply(curPrice);

                addLogs(null, logs, next, incrementValue, incrementCost);

                next.setIncrementValue(incrementValue);
                next.setIncrementCost(incrementCost);
                changeDay.add(next.getMeasureTime().toLocalDate());
                baseMapper.updateById(next);
            }

            // 修改天数据
            updateDayData(changeDay, l.getDeviceId(), l.getDataTypeId(), logs);

            DeviceEntity device = deviceService.getById(l.getDeviceId());
            DataTypeEntity dataType = dataTypeService.getById(l.getDataTypeId());

            dataRecordService.add(DataRecordTypeEnum.DEVICE_DATA_MODIFY, new StringBuilder().append(device.getName()).append("(")
                    .append(dataType.getName()).append(")").append(DateUtils.dateToString(l.getMeasureTimeStart(), DateUtils.PATTERN)).append("~")
                    .append(DateUtils.dateToString(l.getMeasureTimeEnd(), DateUtils.PATTERN)).toString(), l.getDeviceId() + "-" + l.getDataTypeId(), logs);
        });
    }

    /**
     * 批量修改小时数据
     * 会有个别可能不运行的设备会漏处理，需要手动处理
     *
     * @param dateTimeStr
     */
    @Override
    @Transactional
    public void modifyHourData(String dateTimeStr) {
        // 获取time中的日期和时间
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr, formatter);
        LocalDate date = dateTime.toLocalDate();
        LocalTime time = dateTime.toLocalTime();

        // 从hour表，获取上一周期数据
        QueryWrapper<DeviceMeasureDataIncrementHour> query = new QueryWrapper<>();
        query.lambda()
                .eq(DeviceMeasureDataIncrementHour::getMeasureTime, dateTime.minusHours(1));
        List<DeviceMeasureDataIncrementHour> deviceMeasureDataIncrementHours = deviceMeasureDataIncrementHourService.list(query);
        Map<String, DeviceMeasureDataIncrementHour> deviceIdMap = deviceMeasureDataIncrementHours.stream().collect(Collectors.toMap(DeviceMeasureDataIncrementHour::getDeviceId, v -> v));
        List<String> deviceIdList = deviceMeasureDataIncrementHours.stream().map(DeviceMeasureDataIncrementHour::getDeviceId).collect(Collectors.toList());
        log.info("从hour表，获取上一周期数据已完成");
        // 从hour表，获取当前周期数据
        QueryWrapper<DeviceMeasureDataIncrementHour> queryCur = new QueryWrapper<>();
        queryCur.lambda()
                .eq(DeviceMeasureDataIncrementHour::getMeasureTime, dateTime);
        List<DeviceMeasureDataIncrementHour> deviceMeasureDataIncrementHoursCur = deviceMeasureDataIncrementHourService.list(queryCur);
        Map<String, DeviceMeasureDataIncrementHour> deviceIdMapCur = deviceMeasureDataIncrementHoursCur.stream().collect(Collectors.toMap(DeviceMeasureDataIncrementHour::getDeviceId, v -> v));
        log.info("从hour表，获取当前周期数据已完成");

        // 从history表，获取当前周期指定设备的数据
        List<DeviceMeasureDataHistoryEntity> historyEntityList = deviceMeasureDataHistoryMapper.selectHourDataFirst(deviceIdList, dateTime, dateTime.plusHours(1));
        Map<String, DeviceMeasureDataHistoryEntity> deviceIdHistoryMap =
                historyEntityList.stream().collect(Collectors.toMap(DeviceMeasureDataHistoryEntity::getDeviceId, v -> v, (existing, replacement) -> existing));
        log.info("history表，获取当前周期指定设备的数据已完成");

        List<DeviceMeasureDataIncrementHour> incrementHoursUpdate = new ArrayList<>();
        deviceIdMapCur.forEach((k, v) -> {
            // 当前周期数据
            DeviceMeasureDataHistoryEntity HistoryEntity = deviceIdHistoryMap.get(k);
            v.setMeasureValue(HistoryEntity == null ? BigDecimal.ZERO : HistoryEntity.getMeasureValue());
            BigDecimal beforeDate;
            // 当期有，上周期没有，上周期为0
            if (deviceIdMap.get(k) == null) {
                beforeDate = BigDecimal.ZERO;
            } else {
                beforeDate = deviceIdMap.get(k).getMeasureValue();
            }
            v.setIncrementValue(HistoryEntity == null ? BigDecimal.ZERO : HistoryEntity.getMeasureValue().subtract(beforeDate));
            setHourPrice(dateTime, v.getDataTypeId(), v);
            v.setUpdateTime(LocalDateTime.now());
            incrementHoursUpdate.add(v);
        });
        log.info("开始更新小时表数据");
        deviceMeasureDataIncrementHourService.updateBatchById(incrementHoursUpdate);


    }


    /**
     * 设置小时值的费用，根据dataType值进行设置费用
     *
     * @param date
     * @param dataTypeId
     * @param entity
     */
    private void setHourPrice(LocalDateTime date, String dataTypeId, DeviceMeasureDataIncrementHour entity) {
        BigDecimal incrementValue = entity.getIncrementValue();
        BigDecimal price = energyPriceService.getEnergyPriceByDataTypeId(dataTypeId, date);
        if (price != null) {
            entity.setIncrementCost(incrementValue.multiply(price));
        }
    }

    private static void addLogs(BigDecimal lastVal, List<DataRecordLogEntity> logs, DeviceMeasureDataIncrementHour old, BigDecimal incrementValue, BigDecimal incrementCost) {
        if (lastVal != null) {
            DataRecordLogEntity val = new DataRecordLogEntity();
            val.setTag(DateUtils.dateToString(old.getMeasureTime(), DateUtils.PATTERN) + "(实际值)");
            val.setOldData(old.getMeasureValue() == null ? null : old.getMeasureValue().toString());
            val.setNewData(lastVal.toString());
            logs.add(val);
        }

        DataRecordLogEntity increment = new DataRecordLogEntity();
        increment.setTag(DateUtils.dateToString(old.getMeasureTime(), DateUtils.PATTERN) + "(增量值)");
        increment.setOldData(old.getIncrementValue() == null ? null : old.getIncrementValue().toString());
        increment.setNewData(incrementValue == null ? null : incrementValue.toString());
        logs.add(increment);

        DataRecordLogEntity cost = new DataRecordLogEntity();
        cost.setTag(DateUtils.dateToString(old.getMeasureTime(), DateUtils.PATTERN) + "(费用)");
        cost.setOldData(old.getIncrementCost() == null ? null : old.getIncrementCost().toString());
        cost.setNewData(incrementCost == null ? null : incrementCost.toString());
        logs.add(cost);
    }

    /**
     * 修改天数据
     *
     * @param changeDay
     * @param deviceId
     * @param dataTypeId
     * @param logs
     */
    private void updateDayData(Set<LocalDate> changeDay, String deviceId, String dataTypeId, List<DataRecordLogEntity> logs) {

        List<DeviceMeasureDataDayDTO> dayDatas = baseMapper.getMeasureDataDayPriceByDeviceAndDataType(changeDay, dataTypeId, deviceId);

        for (DeviceMeasureDataDayDTO dayData : dayDatas) {
            QueryWrapper<DeviceMeasureDataDayEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(DeviceMeasureDataDayEntity::getDeviceId, deviceId)
                    .eq(DeviceMeasureDataDayEntity::getDataTypeId, dataTypeId)
                    .eq(DeviceMeasureDataDayEntity::getMeasureTime, dayData.getDay());
            DeviceMeasureDataDayEntity old = dataDayMapper.selectOne(queryWrapper);

            DataRecordLogEntity val = new DataRecordLogEntity();
            val.setTag(DateUtils.dateToString(old.getMeasureTime(), DateUtils.YMD_PATTERN) + "(实际值)");
            val.setOldData(old.getMeasureValue() == null ? null : old.getMeasureValue().toString());
            val.setNewData(dayData.getMax().toString());
            logs.add(val);

            DataRecordLogEntity increment = new DataRecordLogEntity();
            increment.setTag(DateUtils.dateToString(old.getMeasureTime(), DateUtils.YMD_PATTERN) + "(增量值)");
            increment.setOldData(old.getIncrementValue() == null ? null : old.getIncrementValue().toString());
            increment.setNewData(dayData.getIncrement() == null ? null : dayData.getIncrement().toString());
            logs.add(increment);

            DataRecordLogEntity cost = new DataRecordLogEntity();
            cost.setTag(DateUtils.dateToString(old.getMeasureTime(), DateUtils.YMD_PATTERN) + "(费用)");
            cost.setOldData(old.getIncrementCost() == null ? null : old.getIncrementCost().toString());
            cost.setNewData(dayData.getCost() == null ? null : dayData.getCost().toString());
            logs.add(cost);

            UpdateWrapper<DeviceMeasureDataDayEntity> updateParam = new UpdateWrapper<>();
            updateParam.lambda().set(DeviceMeasureDataDayEntity::getIncrementCost, dayData.getCost())
                    .set(DeviceMeasureDataDayEntity::getMeasureValue, dayData.getMax())
                    .set(DeviceMeasureDataDayEntity::getIncrementValue, dayData.getIncrement())
                    .eq(DeviceMeasureDataDayEntity::getDeviceId, deviceId)
                    .eq(DeviceMeasureDataDayEntity::getDataTypeId, dataTypeId)
                    .eq(DeviceMeasureDataDayEntity::getMeasureTime, dayData.getDay());
            dataDayMapper.update(updateParam);
        }
    }

    private List<DeviceMeasureDataIncrementHour> getOldData(DeviceMeasureDataHourDTO dto) {
        QueryWrapper<DeviceMeasureDataIncrementHour> query = new QueryWrapper<>();
        query.lambda().eq(DeviceMeasureDataIncrementHour::getDeviceId, dto.getDeviceId())
                .eq(DeviceMeasureDataIncrementHour::getDataTypeId, dto.getDataTypeId())
                .ge(DeviceMeasureDataIncrementHour::getMeasureTime, dto.getMeasureTimeStart())
                .lt(DeviceMeasureDataIncrementHour::getMeasureTime, dto.getMeasureTimeEnd());
        List<DeviceMeasureDataIncrementHour> old = baseMapper.selectList(query);

        return old;
    }

    @Override
    public PageResult<DeviceMeasureDataIncrementHourVO> getPageList(DeviceMeasureDataIncrementHourPageParam param) {
        // 参数校验
        if (param == null
                || param.getStartTime() == null
                || param.getEndTime() == null) {
            throw new IllegalArgumentException("参数不能为空或包含空值");
        }
        try {

            List<String> deviceIds = deviceMapper.selectDeviceIdByCodeOrName(param.getCode(), param.getName());

            // 调用分页查询方法
            PageResult<DeviceMeasureDataIncrementHour> result = customPage(param,
                    (page, aParam) -> baseMapper.queryPageListByDeviceId(
                            page,
                            aParam.getStartTime(),
                            aParam.getEndTime(),
                            deviceIds
                    )
            );
            // 如果结果列表为空，直接返回空分页结果
            if (CollectionUtil.isEmpty(result.getList())) {
                return new PageResult<>(0L, Collections.emptyList());
            }

            // 将 Entity 转换为 VO（视图对象）
            List<DeviceMeasureDataIncrementHourVO> list = result.getList().stream()
                    .map(entity -> {
                        DeviceMeasureDataIncrementHourVO vo = new DeviceMeasureDataIncrementHourVO();
                        BeanUtils.copyProperties(entity, vo);
                        if (deviceService != null) {
                            DeviceEntity device = deviceService.getById(entity.getDeviceId());
                            if (device != null) {
                                vo.setDeviceCode(device.getCode());
                                vo.setDeviceName(device.getName());
                            }
                        }
                        return vo;
                    })
                    .collect(Collectors.toList());

            return new PageResult<>(result.getTotal(), list);
        } catch (
                Exception e) {
            // 异常处理，记录日志并抛出自定义异常
            log.error("查询分页数据失败", e);
            throw new RuntimeException("查询分页数据失败", e);
        }
    }

    @Autowired
    private DataRecordLogService dataRecordLogService;

    @Transactional
    public boolean updateMeasureIncrementHourData(com.zxy.ziems.server.energy.pojo.param.DeviceMeasureDataIncrementHour data) {

        if (data.getDeviceId() == null || data.getMeasureTime() == null || data.getUpdateTime() == null) {
            throw new IllegalArgumentException("设备ID、测量时间和更新时间不能为空");
        }

        //在事务中后续要修改这条记录， FOR UPDATE加行锁排它
        DeviceMeasureDataIncrementHour oldData = deviceMeasureDataIncrementHourMapper.selectByDeviceIdAndTime(data.getDeviceId(),
                data.getMeasureTime());

        // 调用MyBatis方法执行更新
        int updatedRows = deviceMeasureDataIncrementHourMapper.updateDeviceMeasureData(
                data.getDeviceId(),
                data.getMeasureTime(),
                data.getMeasureValue(),
                data.getAvgValue(),
                data.getIncrementValue(),
                data.getIncrementCost(),
                TokenUtils.getUserIdByToken(),
                data.getUpdateTime());// 传入当前数据库中的 update_time

        if (updatedRows == 0) {
            throw new RuntimeException("数据已被修改，请刷新后重试");
        }
        // 变更比较 + 写入审计
        if (oldData != null) {

            List<DataRecordLogEntity> logs = new ArrayList<>();

            compareAndLog(logs, oldData.getMeasureValue(), data.getMeasureValue(), data.getDeviceId(), "测量值");
            compareAndLog(logs, oldData.getIncrementValue(), data.getIncrementValue(), data.getDeviceId(), "增量值");
            compareAndLog(logs, oldData.getIncrementCost(), data.getIncrementCost(), data.getDeviceId(), "增量费用");
            compareAndLog(logs, oldData.getAvgValue(), data.getAvgValue(), data.getDeviceId(), "平均值");

            if (!logs.isEmpty()) {
                dataRecordLogService.adds(logs);
            }
        }
        return true;
    }

    // 提取的比较方法
    private void compareAndLog(List<DataRecordLogEntity> logs, Object oldVal, Object newVal, String deviceId, String tag) {
        if (!Objects.equals(oldVal, newVal)) {
            DataRecordLogEntity logEntity = new DataRecordLogEntity();
            logEntity.setDataRecordId(deviceId);
            logEntity.setOldData(String.valueOf(oldVal));
            logEntity.setNewData(String.valueOf(newVal));
            logEntity.setTag(tag);
            logs.add(logEntity);
        }
    }
}
